#[macro_use]
extern crate lazy_static; 

mod ast;
mod llvm;
mod riscv;
mod sym_type;
mod stack;
#[macro_use]
mod config;
mod util;
mod float;
mod frontend;
mod midend;
mod backend;

use lalrpop_util::lalrpop_mod;

use std::io;
use std::fs;
use std::thread;
use std::sync::mpsc;
use std::env::args;
use std::fs::read_to_string;

use config::*;
use frontend::{generate_program};
use midend::{optimise};
use backend::{emit_asm};
use midend::dump::*;

lalrpop_mod!(sysy);

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut args = args();
    args.next();
    let input_file = args.next().unwrap();
    let input = read_to_string(&input_file)?;
    let mode = args.next().unwrap();
    vprintln!("Parsing sysy code...");
    let mut ast = sysy::SysYParser::new().parse(&input).unwrap();
    vprintln!("Finished\n");

    vprintln!("Building llvm code...");
    let (tx, rx) = mpsc::channel();
    let builder = thread::Builder::new().stack_size(16*1024*1024); // 16MB
    builder.spawn(move || {
        let program = generate_program(&mut ast).unwrap();
        tx.send(program).unwrap();
    }).unwrap();
    let mut program = rx.recv().unwrap();
    vprintln!("Finished\n");

    let config = get_config_manager();
    if config.optimise {
        program = optimise(program);
    }

    if mode == "-llvm" {
        args.next();
        let split_output = input_file.split('.').collect::<Vec<_>>();
        let default_output = String::from(split_output[0])+".ll";
        let output = args.next().unwrap_or(default_output);
        let mut out = fs::File::create(&output)?;

        vprintln!("Writing llvm code to {}...", output);
        program.dump(&mut out)?;
        vprintln!("Finished\n");
    } else if mode == "-std" {
        let mut out = io::stdout();
        program.dump(&mut out)?;
    } else if mode == "-S" {
        args.next();
        let split_output = input_file.split('.').collect::<Vec<_>>();
        let default_output = String::from(split_output[0])+".s";
        let output = args.next().unwrap_or(default_output);
        
        vprintln!("Building llvm code...");
        let asm = emit_asm(&program);
        vprintln!("Finshed\n");
    
        vprintln!("Writing riscv asm code to {}...", output);
        let mut out = fs::File::create(&output)?;
        asm.dump(&mut out)?;
        vprintln!("Finished\n");
    }

    Ok(())
}
